package cn.skyquant.quant4j.jforex.sdk.strategy2;

import cn.skyquant.quant4j.jforex.sdk.strategy.OrderEx;
import com.dukascopy.api.*;
import com.dukascopy.api.feed.IBarFeedListener;

import java.util.*;

public abstract class MultiConfigStrategy extends QuantStrategy implements IStrategy, IBarFeedListener {

    @Configurable("大K线")
    public Period bigPeriod = Period.DAILY;
    @Configurable("小K线")
    public Period period = Period.ONE_HOUR;

    protected final List<InstrumentConfig> configList = new ArrayList<InstrumentConfig>();
    protected final Map<String, InstrumentConfig> configMap = new HashMap<>();
    final Set<Instrument> instrumentSet = new HashSet<>();

    protected abstract void init(InitEntity initEntity);

    protected class InitEntity {
        private final List<InstrumentConfig> a = new ArrayList<>();
        private final List<String> reasonList = new ArrayList<>();

        private InitEntity() {
        }

        public void addReason(String reason) {
            reasonList.add(reason);
        }

        public void add(InstrumentConfig config){
            this.a.add(config);
        }
    }

    protected abstract void process(InstrumentConfig x, IBar bar) throws JFException;

    protected abstract void dailyOpen(Instrument instrument, long time) throws JFException;

    @Override
    public final boolean onStartEx() {
        InitEntity initEntity = new InitEntity();
        init(initEntity);
        if (!initEntity.reasonList.isEmpty()) {
            initEntity.reasonList.forEach(x -> {
                err(x,x);
            });
            warn("[%s %s] must be quit!",
                    "[%s %s] 策略必须退出",
                    getName(), getVersion());
            return false;
        }

        initEntity.a.stream().filter(x -> x.isValid()).forEach(x -> {
            configList.add(x);
            configMap.put(x.toString(), x);
            instrumentSet.add(x.instrument);
        });

        if (configList.isEmpty()) {
            warn("configList is empty [%s %s] quit!",
                    "因为没找到一个配置项,所以策略[%s %s]退出!",
                    getName(), getVersion());
            return false;
        }

        if (instrumentSet.isEmpty()) {
            warn("instrumentSet is empty [%s %s] quit!",
                    "因为没配置任何一个货币,所以策略[%s %s]退出!",
                    getName(), getVersion());
            return false;
        }

        context.setSubscribedInstruments(instrumentSet);
        for (Instrument instrument : instrumentSet) {
            context.subscribeToBarsFeed(instrument, period, OfferSide.ASK, this);//逆市运行间隔
            context.subscribeToBarsFeed(instrument, bigPeriod, OfferSide.ASK, this);//逆市运行间隔
            out("subscribeToBarsFeed:%s",instrument.name());
            try {
                long time = history.getPreviousBarStart(bigPeriod, context.getTime());
                dailyOpen(instrument, time);
            } catch (JFException e) {
                out("MultiConfigStrategy.onStartEx() daily open error:%s",
                        "MultiConfigStrategy.onStartEx() 大K线开盘计算错误:%s",
                        e.getMessage());
            }

        }
        return true;
    }

    @Override
    public void onBar(
            Instrument instrument,
            Period period,
            OfferSide offerSide,
            IBar bar
    ) {

        if (period.equals(bigPeriod)) {
            try {
                dailyOpen(instrument, bar.getTime());
            } catch (JFException e) {
                err("MultiConfigStrategy.dailyOpen error:%s",
                        "MultiConfigStrategy.onBar() 大K线开盘计算错误:%s",
                        e.getMessage());
            }
            return;
        }
        //停盘时间不干活
        if (dataService.isOfflineTime(context.getTime())) {
            return;
        }

        if (period.equals(this.period)) {
            for (InstrumentConfig config : configList) {
                if (config.isValid() && config.instrument == instrument) {
                    try {
                        process(config, bar);
                    } catch (JFException e) {

                    }
                }
            }
        }
    }

    //修改订单止盈止损价
    protected void updateSLTP(List<OrderEx> orders, double sl, double tp) {
        if (orders == null || orders.isEmpty()) {
            return;
        }
        for (OrderEx orderEx : orders) {
            if (orderEx.order == null) {
                continue;
            }
            IOrder order = orderEx.order;
            try {
                if (order.getStopLossPrice() != sl) {
                    order.setStopLossPrice(sl);
                }
                if (order.getTakeProfitPrice() != tp) {
                    order.setTakeProfitPrice(tp);
                }
            } catch (JFException e) {
                warn("update price error,order=%s,sl=%f,tp=%f,message=%s",
                        "修改价格错误,订单=%s,止损=%f,止盈=%f,错误信息=%s",
                        orderEx, sl, tp, e.getMessage());
            }
        }
    }

    //平仓所有订单
    protected final void closeOrders(Collection<IOrder> orderList) {
        do {
            boolean canBreak = true;
            for (IOrder order : orderList) {
                IOrder.State state = order.getState();
                if (state == IOrder.State.FILLED) {
                    try {
                        order.close();
                        order.waitForUpdate(2000, IOrder.State.CLOSED);
                    } catch (JFException e) {

                    }
                    if (order.getState() == IOrder.State.CLOSED) {
                        canBreak = false;
                    }
                } else if (state == IOrder.State.OPENED) {
                    try {
                        order.close();
                        order.waitForUpdate(2000, IOrder.State.CANCELED);
                    } catch (JFException e) {
                        warn("closeOrder:%s ,error:%s",
                                "平仓订单[%s] 错误:%s",
                                order.toString(), e.getMessage());
                    }
                    if (order.getState() == IOrder.State.CANCELED) {

                    } else {
                        canBreak = false;
                    }
                }
            }
            if (canBreak) {
                break;
            }
        } while (true);
    }

    //从指定时间到当前时间，找最低价
    protected double getLowPrice(Instrument instrument, long fromtime, Period p) {
        try {
            long from = history.getNextBarStart(p, fromtime);
            long to = history.getBarStart(p, context.getTime());
            if (from > to) {
                return 100000;
            }
            List<IBar> iBarList = history.getBars(instrument, p, OfferSide.ASK, Filter.WEEKENDS, from, to);//计算从开首仓到最后一仓这些日子的bar
            return iBarList.stream().mapToDouble(x -> x.getLow()).min().getAsDouble();
        } catch (JFException e) {
            err("MultiConfigStrategy.getLowPrice error %s:",
                    "MultiConfigStrategy.getLowPrice 获取最低价错误 %s:",
                    e.getMessage());
            return 100000;
        }
    }

    //从指定时间到当前时间，找最高价
    protected double getHighPrice(Instrument instrument, long fromtime, Period p) {
        try {
            long to = history.getBarStart(p, context.getTime());
            long from = history.getNextBarStart(p, fromtime);
            if (from > to) {
                return 0;
            }
            List<IBar> iBarList = history.getBars(instrument, p, OfferSide.ASK, Filter.WEEKENDS, from, to);//计算从开首仓到最后一仓这些日子的bar
            return iBarList.stream().mapToDouble(x -> x.getHigh()).max().getAsDouble();
        } catch (JFException e) {
            err("MultiConfigStrategy.getHighPrice error %s:",
                    "MultiConfigStrategy.getHighPrice获取最高价错误%s:",
                    e.getMessage());
            return 0;
        }
    }
}
